#ifndef ANIMATIONSYSTEM_H
#define ANIMATIONSYSTEM_H
#pragma once

#include <vector>
#include <fstream>

////////////////////////////////////////////////////////////////
// Stores the state of the animation at a given time
////////////////////////////////////////////////////////////////
struct KeyFrame
{
	float keyTime;
	float tweenTime;
	unsigned int numNodes;
	std::vector<vec3f> nodeVec;

	KeyFrame() : keyTime(0.0), tweenTime(0.0), numNodes(0) {}
};

////////////////////////////////////////////////////////////////
// Stores the animation data
////////////////////////////////////////////////////////////////
class Animation
{	friend class Interpolator;
private:
	unsigned int numKeyFrames;
	float duration;
	std::vector<KeyFrame> keyFrames;

public:
	Animation(void){ }
	~Animation(void){ }

	////////////////////////////////////////////////////////////////
	// Loads the animation from a file
	//
	// Parameters
	// string fileName - the name of the animation file
	////////////////////////////////////////////////////////////////
	void LoadFromFile(std::string fileName)
	{ 
		std::ifstream readObject;
		readObject.open(fileName, std::ios_base::in | std::ios_base::binary);

		if (!readObject.is_open())
			return;

		readObject.read((char*)&duration, sizeof(duration));
		readObject.read((char*)&numKeyFrames, sizeof(numKeyFrames));

		keyFrames.reserve(numKeyFrames);

		for (unsigned int i = 0; i < numKeyFrames; ++i)
		{
			KeyFrame tempFrame;

			readObject.read((char*)&tempFrame.keyTime, sizeof(tempFrame.keyTime));
			readObject.read((char*)&tempFrame.numNodes, sizeof(tempFrame.numNodes));
		
			for (unsigned int nodeCount = 0; nodeCount < tempFrame.numNodes; ++nodeCount)
			{
				vec3f tempNode;

				readObject.read((char*)&tempNode, sizeof(tempNode));
				tempFrame.nodeVec.push_back(tempNode);
			}

			keyFrames.push_back(tempFrame);
		}

		readObject.close();

		for (unsigned int i = 0; i < numKeyFrames-1; ++i)
		{
			keyFrames[i].tweenTime = (keyFrames[i+1].keyTime - keyFrames[i].keyTime);
		}

		keyFrames[numKeyFrames-1].tweenTime = (duration - keyFrames[numKeyFrames-1].keyTime);
	}
};

class Interpolator
{
private:
	float currTime;
	Animation const* animation;
	KeyFrame currKeyFrame;

public:

	Interpolator(void){ }
	~Interpolator(void){ }

	////////////////////////////////////////////////////////////////
	// Attaches an animation to the interpolator
	//
	// Parameters
	// const Animation *inAnimation - the animation to attach to
	// 
	//	YOU SHOULD NOT COPY ALL ANIMATION DATA INTO THE INTERPOLATOR.
	//  THE INTERPOLATOR ONLY NEEDS A CONST* TO THE ANIMATION.
	////////////////////////////////////////////////////////////////
	void SetAnimation(const Animation *inAnimation)
	{
		animation = inAnimation;
		currKeyFrame = animation->keyFrames[0];
	}

	////////////////////////////////////////////////////////////////
	// Adds time to the interpolator
	//
	// Parameters
	// float t - the time to add
	////////////////////////////////////////////////////////////////
	void AddTime(float t) 
	{
		currTime += t;
	}

	////////////////////////////////////////////////////////////////
	// Sets the interpolators time
	//
	// Parameters
	// float t - the time to set to
	////////////////////////////////////////////////////////////////
	void SetTime(float t) 
	{
		currTime = t;
	}

	////////////////////////////////////////////////////////////////
	// Interpolates between the animations key-frames based on the 
	// interpolator's current time
	// 
	// BASED ON THE CURRENT TIME, PROCESS SHOULD CALCULATE WHICH
	// TWO FRAMES THE TIME IS BETWEEN AND THEN STORE THE RESULT
	// OF THE INTERPOLATION OF THOSE TWO FRAMES IN THE
	// CURRENTKEYFRAME MEMBER.
	//
	// VEC3 AND MATRIX4 BOTH HAVE INTERPOLATE FRIEND
	// FUNCTIONS, SO BE SURE TO MAKE USE OF THEM.
	//
	// See "Keyframe Selection.ppt" for an overview of how to
	// find the correct keyframes to interpolate.
	//
	////////////////////////////////////////////////////////////////
	void Process(void)
	{	
		float lambda = 0.0f;

		// Error Check Time
		if (currTime < 0.0f)
			currTime += this->animation->duration;

		if (currTime > this->animation->duration)
			currTime -= this->animation->duration;

		// Find current frame
		for (unsigned int i = 0; i < this->animation->numKeyFrames; ++i)
		{
			if (currTime < (animation->keyFrames[i].keyTime + animation->keyFrames[i].tweenTime))
			{
				// Set frame
				const KeyFrame& frame1 = animation->keyFrames[i];

				// Roll over the index
				unsigned int index = i+1;
				if (index >= animation->numKeyFrames)
					index = 0;

				const KeyFrame& frame2 = animation->keyFrames[index];

				lambda = (currTime - frame1.keyTime) / frame1.tweenTime;

				for (unsigned int j = 0; j < this->currKeyFrame.numNodes; ++j)
				{
					// Interpolate into currKeyFrame
					currKeyFrame.nodeVec[j] = interpolate(frame1.nodeVec[j], frame2.nodeVec[j], lambda);
				}

				break;
			}
		}
	}

	KeyFrame* GetCurrKeyFrame()	{ return &currKeyFrame; }
};

#endif